/*
 * This file is part of the xOperator SPARQL/XMPP agent.
 * For further information see: http://xoperator.aksw.org
 * Copyright (C) 2007-2008  Jörg Unbehauen

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.aksw.xoperator;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.aksw.xoperator.aiml.AimlFacade;
import org.aksw.xoperator.aiml.AliceBotFactory;
import org.aksw.xoperator.aiml.ExternalListener;
import org.aksw.xoperator.aiml.ExternalListenerGroovy;
import org.aksw.xoperator.command.Command;
import org.aksw.xoperator.command.QueryCommand;
import org.aksw.xoperator.conf.AgentConfiguration;
import org.aksw.xoperator.conf.ConfigurationSerializer;
import org.aksw.xoperator.roster.RosterManager;
import org.aksw.xoperator.security.SimpleSecurityProvider;
import org.aksw.xoperator.sparql.SparqlEndpointFacade;
import org.aksw.xoperator.sparql.http.RemoteSparqlEndpoint;
import org.aksw.xoperator.sparql.local.LocalSparqlEndpoint;
import org.aksw.xoperator.sparql.p2p.P2PFacade;
import org.aksw.xoperator.transform.SimpleXslTransformer;
import org.aksw.xoperator.xmpp.JabberClientManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.Parameter;
import org.picocontainer.behaviors.Caching;
import org.picocontainer.parameters.BasicComponentParameter;
import org.picocontainer.parameters.ComponentParameter;


/**
 * This class ties the components of the agent together and starts them.
 *
 * @author joerg
 */
public class Agent {
    private MutablePicoContainer container = new DefaultPicoContainer(new Caching());
    private Log log = LogFactory.getLog(Agent.class);
    private String confDir = "./conf";

    public Agent() {
        super();
    }

    public Agent(String confDir) {
        super();
        this.confDir = confDir;
    }

    /**
     * Starts the components, that need starting. For example the agent will log into the netork.
     */
    public void start() {
    	try{
    		validateConf();
	        registerComponents(container);
	        registerCommands(container);
	        container.getComponents();
	        container.start();
    	} catch (Exception e){
    		log.error("Exception caught:",e);
    	}
    }

    private void registerCommands(MutablePicoContainer container2) {
        //List<Class> commands = new ArrayList();

        //commands.add(new QueryCommand);
        container.addComponent(QueryCommand.class);
    }

    private void registerComponents(MutablePicoContainer container) {
        container.addComponent(Controller.class, Controller.class,
            new Parameter[] {
                new ComponentParameter(Command.class, false),
                new BasicComponentParameter(), new BasicComponentParameter(),
                new BasicComponentParameter(),new BasicComponentParameter()
            });
        container.addComponent(loadConfigurations());
        container.addComponent(JabberClientManager.class);
        container.addComponent(SparqlEndpointFacade.class);
        container.addComponent(SimpleXslTransformer.class);
        container.addComponent(AimlFacade.class);
        container.addComponent(ExternalListenerGroovy.class);
        container.addComponent(SimpleSecurityProvider.class);
        container.addComponent(AliceBotFactory.class, AliceBotFactory.class,
            new Parameter[] {
                new BasicComponentParameter(),
                new ComponentParameter(ExternalListener.class, false)
            });
        container.addComponent(LocalSparqlEndpoint.class);
        container.addComponent(RemoteSparqlEndpoint.class);
        container.addComponent(RosterManager.class);
        container.addComponent(P2PFacade.class);
        container.addComponent(new ThreadPoolExecutor(5, 10, 60,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100)));
    }

    private AgentConfiguration loadConfigurations() {
    	File confDirFile = new File(confDir);
  
        log.info("Loading configuration from: " + confDirFile.getAbsolutePath());
        ConfigurationSerializer confSer = new ConfigurationSerializer(confDirFile.getAbsolutePath());
        return confSer.load();
    }

    /**
     * main method for starting from the command line.
     *
     * @param args The first String can be the location of a the config file.
     */
    public static void main(String[] args) {
        Agent agent;

        if ((args.length > 0) && !args[0].isEmpty()) {
            agent = new Agent(args[0]);
        } else {
            agent = new Agent();
        }

        agent.start();

        try {
            System.in.read();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    /*
     * simple check if the conf folder is valid
     */
    private void validateConf(){
    	File agentConfFile = new File(confDir+File.separator+"agent.conf.xml");
    	
    	

    		if (agentConfFile.exists()){
    			log.warn("Starting the Agent, loading config data from: " + confDir);
    	    	log.info("Absoulte path of conf dir is: " + new File(confDir).getAbsolutePath());
    		}else{
    			log.error("No configuration found in: " + confDir);
    			System.exit(0);
    		}

    }
}
